package com.crb.kms.hsm.codec;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;

import com.crb.kms.hsm.sjj1312b.codec.response.HsmConvertRsaPriKeyUseKekResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmDisperseKeyResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmEncOrDecryUsingSmkResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmEncryOrDisperseResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmGenEccKeyResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmGenRandomKeyResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmGenRsaPairResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmGetSmkResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmImportShsmkResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmMacCalculateResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmResponse;
import com.crb.kms.hsm.sjj1312b.codec.response.HsmSignatureUsingPrivatekeyResponse;
import com.crb.log.Log;
import com.crb.util.CrbUtil;

/**
 * 实现SJJ1312-B加密机相关API
 * @author
 * @version 1.0.0 2014-1-14 下午3:51:17
 */
public final class HsmSJJ1312BUtil {

	/**
	 * 导入次主密钥(A018)
	 * 
	 * @param shsmkIndex1
	 *            导入主密钥索引号
	 * @param shsmkIndex2
	 *            保护主密钥索引号
	 * @param keyLenFlag
	 *            导入主密钥长度<br>
	 *            <li>1: 16字节</li><br>
	 *            <li>2: 32字节</li>
	 * @param shsmk
	 *            导入主密钥密文,用shsmkIndex2对shsmkIndex1对应的密钥值采用3DES-ECB算法加密
	 * @param shsmkMac
	 *            导入主密钥密文MAC,用shsmkIndex2对shsmk采用PBOC MAC算法计算
	 * @return
	 */
	public static HsmImportShsmkResponse importShsmk(int shsmkIndex1, int shsmkIndex2,
			byte keyLenFlag, String shsmk, String shsmkMac) {
		HsmImportShsmkResponse resp = new HsmImportShsmkResponse();
		try {
			if (keyLenFlag != 1 && keyLenFlag != 2) {
				resp.setStatus(HsmResponse.RLT_FAIL);
				throw new IllegalArgumentException("keyLenFlag: 1 or 2.");
			}

			StringBuilder data = new StringBuilder("A018");
			data.append(CrbUtil.long2HexString(shsmkIndex1, 2));
			data.append(CrbUtil.long2HexString(shsmkIndex2, 2));
			data.append(CrbUtil.long2HexString(keyLenFlag, 1));
			data.append(shsmk);
			data.append(shsmkMac);
			String hsmResp = sendData2Hsm(data.toString());

			resultCheck(resp, hsmResp, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("importShsmk().", e);
		}
		return resp;
	}

	/**
	 * 用指定的次主密钥分散并对分散结果加密(B060)
	 * 
	 * @param shsmkIndex1
	 *            次主密钥索引一,分散根密钥索引号
	 * @param shsmkIndex2
	 *            次主密钥索引二,用于保护分散出的密钥索引号
	 * @param disperseData
	 *            分散因子(长度需为8字节整数倍,最多只能进行三次分散)
	 * @return 分散后数据采用shsmkIndex2对应的密钥进行3DES加密
	 */
	public static HsmDisperseKeyResponse disperseKeyForEncry(int shsmkIndex1, int shsmkIndex2,
			String disperseData) {
		HsmDisperseKeyResponse resp = new HsmDisperseKeyResponse();
		try {
			if (disperseData == null
					|| (disperseData.length() != 16 && disperseData.length() != 32 && disperseData
							.length() != 48)) {
				resp.setStatus(HsmResponse.RLT_FAIL);
				throw new IllegalArgumentException("分散因子长度需为8、16、24字节的倍数.");
			}

			StringBuilder data = new StringBuilder("B060");
			data.append("0000000000000000");
			data.append(CrbUtil.long2HexString(shsmkIndex1, 2));
			data.append(CrbUtil.long2HexString(shsmkIndex2, 2));
			data.append(CrbUtil.long2HexString(disperseData.length() / 16, 1));
			data.append(disperseData);

			String hsmResp = sendData2Hsm(data.toString());
			resultCheck(resp, hsmResp, 8);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setReserve(hsmResp.substring(2, 18));
			resp.setKey(hsmResp.substring(18, 50));
			resp.setKcv(hsmResp.substring(50, 58));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("disperseKeyForEncry().", e);
		}
		return resp;
	}

	/**
	 * 加密或分散数据(A01D)
	 * 
	 * @param shsmkIndex
	 *            次主密钥索引号,当为0xFFFF时为密钥明文
	 * @param flag
	 *            标识<br>
	 *            <li>true: 加密数据</li><br>
	 *            <li>false: 分散密钥并输出</li>
	 * @param keyLength
	 *            密钥长度(8、16、24字节)
	 * @param data
	 *            待加密数据或分散因子
	 * @return 密文数据或分散后的密钥
	 */
	public static HsmEncryOrDisperseResponse encryDataOrdisperseKey(int shsmkIndex, boolean flag,
			int keyLength, String data) {
		HsmEncryOrDisperseResponse resp = new HsmEncryOrDisperseResponse();
		try {
			if (keyLength != 16 && keyLength != 32 && keyLength != 48) {
				throw new IllegalArgumentException("keyLength值错误.");
			}

			if (data == null || data.length() % 8 != 0) {
				throw new IllegalArgumentException("data长度错误.");
			}

			StringBuilder d = new StringBuilder("A01D");
			d.append(CrbUtil.long2HexString(shsmkIndex, 2));
			d.append(flag ? "00" : "01");
			d.append(flag ? "FF" : CrbUtil.long2HexString(data.length() / 16, 1));
			d.append(CrbUtil.long2HexString(keyLength, 1));
			d.append(CrbUtil.long2HexString(data.length() >> 1, 1));
			d.append(data);

			String hsmResp = sendData2Hsm(d.toString());
			resultCheck(resp, hsmResp, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setData(hsmResp.substring(2));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("encryDataOrdisperseKey().", e);
		}
		return resp;
	}

	/**
	 * MAC计算(A050)
	 * 
	 * @param algorithm
	 *            加密算法<br>
	 *            <li>1: DES</li><br>
	 *            <li>2: AES</li>
	 * @param keyType
	 *            密钥类型<br>
	 *            <li>1: BMK</li><br>
	 *            <li>2: TMK</li><br>
	 *            <li>3: SHSMK</li>
	 * @param keyIndex
	 *            密钥索引号
	 * @param keyLength
	 *            密钥长度(其值为8、16、24字节)
	 * @param disperseAlgorithm
	 *            分散算法<br>
	 *            <li>0: 无分散</li><br>
	 *            <li>1: 银联标准</li><br>
	 *            <li>2: 移动标准</li>
	 * @param disperseData
	 *            分散算法为0该域不存在
	 * @param processKeyFlag
	 *            过程密钥标识,分散算法为0时该域不存在<br>
	 *            <li>0: 无过程密钥</li><br>
	 *            <li>1: 有过程密钥</li><br>
	 * @param processKeyAlgorithm
	 *            过程密钥算法,分散算法为0时该域不存在<br>
	 *            <li>0: 3DES（单长度临时密钥）</li><br>
	 *            <li>1: DOUBLE＿ONE＿WAY</li><br>
	 *            <li>2: 1次分散（双长度临时密钥）</li>
	 * @param processKeyLength
	 *            过程密钥标志,分散算法为0时该域不存在
	 * @param processKeyRandom
	 *            过程密钥随机数,分散算法为0时该域不存在
	 * @param macAlgorithm
	 *            MAC算法标志<br>
	 *            <li>1: ANSI99</li><br>
	 *            <li>2: ANSI919</li><br>
	 *            <li>3: XOR</li><br>
	 *            <li>4: IC卡MAC算法</li>
	 * @param icv
	 *            初始向量
	 * @param macData
	 *            MAC计算数据
	 * @return mac
	 */
	public static HsmMacCalculateResponse macCalculate(int algorithm, int keyType, int keyIndex,
			int keyLength, int disperseAlgorithm, String disperseData, int processKeyFlag,
			int processKeyAlgorithm, int processKeyLength, String processKeyRandom,
			int macAlgorithm, String icv, String macData) {
		HsmMacCalculateResponse resp = new HsmMacCalculateResponse();
		try {
			StringBuilder data = new StringBuilder("A050");
			data.append(CrbUtil.long2HexString(algorithm, 1));// 加密算法
			data.append(CrbUtil.long2HexString(keyType, 1));// 密钥类型
			data.append(CrbUtil.long2HexString(keyIndex, 2));// 密钥索引
			data.append(CrbUtil.long2HexString(keyLength, 1));// 密钥长度
			data.append(CrbUtil.long2HexString(disperseAlgorithm, 1));// 分散算法
			if (disperseAlgorithm != 0) {
				data.append(CrbUtil.long2HexString(disperseData.length() / 16, 1));// 分散次数
				data.append(disperseData);// 分散数据
				data.append(CrbUtil.long2HexString(processKeyFlag, 1));// 过程密钥标志
				if (processKeyFlag == 1) {
					data.append(CrbUtil.long2HexString(processKeyAlgorithm, 1));// 过程密钥算法
					data.append(CrbUtil.long2HexString(processKeyLength, 1));// 过程密钥长度
					data.append(processKeyRandom);// 过程密钥随机数
				}
			}

			data.append(CrbUtil.long2HexString(macAlgorithm, 1));// MAC算法标志
			data.append(icv);// 初始向量
			data.append(CrbUtil.long2HexString(macData.length() >> 1, 2));// MAC数据长度
			data.append(macData);// MAC数据

			String hsmResp = sendData2Hsm(data.toString());
			resultCheck(resp, hsmResp, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setMac(hsmResp.substring(2, 18));// MAC值
			if (disperseAlgorithm == 2) {
				resp.setIv(hsmResp.substring(18, 34));// IV,移动算法存在该域
			}
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("macCalcu().", e);
		}
		return resp;
	}

	/**
	 * 过程密钥加/解数据(A01B)
	 * 
	 * @param shsmkIndex
	 *            次主密钥索引号
	 * @param disperseCount
	 *            分散次数,取值0/1/2
	 * @param processKeyFlag
	 *            过程密钥标识<br>
	 *            <li>true: 计算过程密钥</li><br>
	 *            <li>false: 无过程密钥</li>
	 * @param processKeyData
	 *            计算过程密钥数据,8字节
	 * @param isEncrypt
	 *            加/解密钥标识<br>
	 *            <li>true: 加密</li><br>
	 *            <li>false: 解密</li>
	 * @param algorithmFlag
	 *            算法标识<br>
	 *            <li>0: 单长度ECB</li><br>
	 *            <li>1: 双长度ECB</li><br>
	 *            <li>2: 单长度CBC</li><br>
	 *            <li>3: 双长度CBC</li><br>
	 *            <li>4: AES</li>
	 * @param iv
	 *            向量
	 * @param data
	 *            待加/解密数据
	 * @param disperseData
	 *            分散数据
	 * @return
	 */
	public static HsmEncOrDecryUsingSmkResponse encOrDecryUsingSmk(int shsmkIndex,
			int disperseCount, boolean processKeyFlag, String processKeyData, boolean isEncrypt,
			int algorithmFlag, String iv, String data, String disperseData) {
		HsmEncOrDecryUsingSmkResponse resp = new HsmEncOrDecryUsingSmkResponse();
		try {
			StringBuilder d = new StringBuilder("A01B");
			d.append(CrbUtil.long2HexString(shsmkIndex, 2));// 次主密钥索引号
			d.append(CrbUtil.long2HexString(disperseCount, 1));// 分散次数
			d.append(processKeyFlag ? "01" : "00");// 过程密钥标识
			if (processKeyFlag) {
				d.append(processKeyData);// 计算过程密钥数据
			} else {
				d.append("0000000000000000");
			}
			d.append(isEncrypt ? "01" : "00"); // 加/解密标识
			d.append(CrbUtil.long2HexString(algorithmFlag, 1));// 算法标识
			if (iv == null) {
				iv = "0000000000000000";
			}
			d.append(iv);// 向量
			d.append(CrbUtil.long2HexString(data.length() >> 1, 2));// 数据长度
			d.append(data);// 数据
			if (disperseCount != 0) {
				d.append(disperseData);
			}

			String hsmResp = sendData2Hsm(d.toString());
			resultCheck(resp, hsmResp, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setData(hsmResp.substring(6));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("encOrDecryUsingSmk().", e);
		}
		return resp;
	}

	/**
	 * 取回指定次主密钥信息,其密钥结果采用主密钥二加密保护(3DES-ECB算法)(B007)
	 * @param smkIndex
	 *            次主密钥索引号
	 * @return
	 */
	public static HsmGetSmkResponse exportSmkEncByMK2(int smkIndex) {
		HsmGetSmkResponse resp = null;
		try {
			resp = new HsmGetSmkResponse();
			String data = "B0070000000000000000" + CrbUtil.long2HexString(smkIndex, 2);
			String hsmResp = sendData2Hsm(data);
			resultCheck(resp, hsmResp, 8);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}
			resp.setSmkKey(hsmResp.substring(18, 50));
			resp.setKcv(hsmResp.substring(50, 58));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("exportSmkEncByMK2().", e);
		}
		return resp;
	}

	/**
	 * 产生一个随机密钥(A016)
	 * @return
	 */
	public static HsmGenRandomKeyResponse genRandom() {
		HsmGenRandomKeyResponse resp = null;
		try {
			resp = new HsmGenRandomKeyResponse();
			String hsmRespData = sendData2Hsm("A016");
			resultCheck(resp, hsmRespData, 8);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setKey(hsmRespData.substring(2));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("genRandom().", e);
		}
		return resp;
	}

	/**
	 * 获取指定的次主密钥,采用LMK进行加密(D230)
	 * @param smkIndex
	 * @return
	 */
	public static HsmGetSmkResponse exportSmkEncByLMK(int smkIndex) {
		HsmGetSmkResponse resp = null;
		try {
			resp = new HsmGetSmkResponse();
			StringBuilder d = new StringBuilder("D230");
			d.append("04");
			d.append(CrbUtil.long2HexString(smkIndex, 2));// 次主密钥索引号

			String hsmRespData = sendData2Hsm(d.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			int keyLen = Integer.parseInt(hsmRespData.substring(2, 4), 16) * 2;
			resp.setSmkKey(hsmRespData.substring(4, 4 + keyLen));
			resp.setKcv(hsmRespData.substring(4 + keyLen));

		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("exportSmkEncByLMK().", e);
		}
		return resp;
	}

	/**
	 * 删除密钥(D22A)
	 * @param keyType
	 *            密钥类型 <br>
	 *            <li>1：RSA密钥</li><br>
	 *            <li>2：次主密钥</li><br>
	 *            <li>3：0xD223指令产生的密钥</li><br>
	 * @param keyIndex
	 *            密钥索引号<br>
	 *            <li>RSA密钥：[0，19]</li><br>
	 *            <li>次主密钥：[0，999]</li><br>
	 *            <li>PBOC2.0的密钥对：[0,486]</li><br>
	 * @param tag
	 *            密钥类型为3时存在,0/1/2/3：密钥对上的指定密钥
	 * 
	 * @return
	 */
	public static boolean deleteKey(int keyType, int keyIndex, int tag) {
		try {
			StringBuilder d = new StringBuilder("D22A");

			d.append(CrbUtil.long2HexString(keyType, 1));
			d.append(CrbUtil.long2HexString(keyIndex, 2));
			d.append(CrbUtil.long2HexString(tag, 1));

			String hsmRespData = sendData2Hsm(d.toString());
			if (hsmRespData == null || !hsmRespData.startsWith("41")) {
				return false;
			}

			int result = Integer.parseInt(hsmRespData.substring(2, 4), 16);
			return result == 0 ? true : false;
		} catch (Exception e) {
			throw new HsmException("deleteKey().", e);
		}
	}

	/**
	 * 导入DES次主密钥,其密钥采用LMK进行3DES-ECB加密(D231)
	 * @param smkIndex
	 *            次主密钥索引号
	 * @param key
	 *            采用LMK加密后的密文密钥
	 * @param kcv
	 *            密钥校验值,长度为8字节
	 * @return
	 */
	public static boolean importSmkEncByLMK(int smkIndex, String key, String kcv) {
		try {
			StringBuilder d = new StringBuilder("D231");
			d.append("04");
			d.append(CrbUtil.long2HexString(smkIndex, 2));
			d.append(CrbUtil.long2HexString(key.length() >> 1, 1));
			d.append(key);
			d.append(kcv);

			String hsmRespData = sendData2Hsm(d.toString());
			if (hsmRespData == null || !hsmRespData.startsWith("41")) {
				return false;
			}

			return true;
		} catch (Exception e) {
			throw new HsmException("importSmkEncByLMK().", e);
		}
	}

	/**
	 * RSA私钥签名(D203)
	 * @param digestAlgorithm
	 *            摘要算法<br>
	 *            <li>0: MD5</li><br>
	 *            <li>1: SHA-1</li><br>
	 *            <li>2: SHA-224</li><br>
	 *            <li>3: SHA-256</li><br>
	 *            <li>4: SHA-384</li><br>
	 *            <li>5: SHA-512</li><br>
	 * @param padding
	 *            填充模式<br>
	 *            <li>0: NO PADDING</li><br>
	 *            <li>1: PADDING(PKCS#1)</li><br>
	 * @param privateKeyLen
	 *            私钥长度
	 * @param privateKey
	 *            私钥(采用LMK加密的私钥DER)
	 * @param data
	 *            待签名数据(数据长度1～256)
	 */
	public static HsmSignatureUsingPrivatekeyResponse signatureUsingPrivatekey(int digestAlgorithm,
			int padding, int privateKeyLen, String privateKey, String data) {
		HsmSignatureUsingPrivatekeyResponse resp = null;
		try {
			resp = new HsmSignatureUsingPrivatekeyResponse();
			StringBuilder d = new StringBuilder("D203");
			d.append(CrbUtil.long2HexString(digestAlgorithm, 1));
			d.append(CrbUtil.long2HexString(padding, 1));
			d.append("FFFF");
			d.append(CrbUtil.long2HexString(privateKey.length() >> 1, 2));
			d.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			d.append(privateKey);
			d.append(data);

			String hsmRespData = sendData2Hsm(d.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			resp.setSignature(hsmRespData.substring(6));
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("signatureUsingPrivatekey().", e);
		}
		return resp;
	}

	public static void encOrDecDataUsePrivateKey(boolean isEnc, int paddingMode, String privateKey,
			String data) {
		try {
			StringBuilder d = new StringBuilder("D20C");
			d.append(isEnc ? "01" : "00");
			d.append(CrbUtil.long2HexString(paddingMode, 1));
			d.append(CrbUtil.long2HexString(privateKey.length() >> 1, 2));
			d.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			d.append(privateKey);
			d.append(data);
			sendData2Hsm(d.toString());
		} catch (Exception e) {
			throw new HsmException("encOrDecDataUsePrivateKey().", e);
		}
	}

	/**
	 * 转换RSA私钥从LMK到KEK加密
	 * 
	 * @param kekIndex
	 *            次主密钥索引号
	 * @param privateKey
	 *            私钥数据(采用LMK加密的DER格式)
	 */
	public static HsmConvertRsaPriKeyUseKekResponse convertRsaPriKeyUseKek(int kekIndex,
			String privateKey) {
		HsmConvertRsaPriKeyUseKekResponse resp = new HsmConvertRsaPriKeyUseKekResponse();
		try {
			StringBuilder data = new StringBuilder("D262");
			data.append(CrbUtil.long2HexString(kekIndex, 2));
			data.append("00");
			data.append(CrbUtil.long2HexString(privateKey.length() >> 1, 2));
			data.append(privateKey);

			String hsmRespData = sendData2Hsm(data.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}
			resp.setPrivateKey(hsmRespData.substring(6));
			return resp;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("convertRsaPriKeyUseKek().", e);
		}
	}

	/**
	 * 采用RSA公钥验证签名(D204)
	 * @param digestAlgorithm
	 *            摘要算法 <br>
	 *            <li>0: MD5</li><br>
	 *            <li>1: SHA-1</li><br>
	 *            <li>2: SHA-224</li><br>
	 *            <li>3: SHA-256</li><br>
	 *            <li>4: SHA-384</li><br>
	 *            <li>5: SHA-512</li><br>
	 * @param padding
	 *            填充模式<br>
	 *            <li>0: NO PADDING</li><br>
	 *            <li>1: PADDING(PKCS#1)</li><br>
	 * @param publicKeyLen
	 *            公钥长度
	 * @param publicKey
	 *            公钥(DER)
	 * @param data
	 *            待验证数据
	 * @param signature
	 *            签名
	 * @return
	 */
	public static boolean verifyUsingPublickey(int digestAlgorithm, int padding, int publicKeyLen,
			String publicKey, String data, String signature) {
		try {
			StringBuilder d = new StringBuilder("D204");
			d.append(CrbUtil.long2HexString(digestAlgorithm, 1));
			d.append(CrbUtil.long2HexString(padding, 1));
			d.append(CrbUtil.long2HexString(publicKeyLen, 2));
			d.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			d.append(CrbUtil.long2HexString(signature.length() >> 1, 2));
			d.append(publicKey);
			d.append(data);
			d.append(signature);

			String hsmRespData = sendData2Hsm(d.toString());
			if (hsmRespData == null || !hsmRespData.startsWith("41")) {
				return false;
			}
			return true;
		} catch (Exception e) {
			throw new HsmException("signatureUsingPrivatekey().", e);
		}
	}

	/**
	 * 产生公私钥对
	 * @param moduleLen
	 *            模长
	 * @param exponent
	 *            指数
	 * @return
	 */
	public static HsmGenRsaPairResponse genRsaKeyPair(int moduleLen, int exponent) {
		HsmGenRsaPairResponse resp = null;
		try {
			resp = new HsmGenRsaPairResponse();
			StringBuilder d = new StringBuilder("D232");
			d.append(CrbUtil.long2HexString(moduleLen, 2));
			d.append(CrbUtil.long2HexString(exponent, 4));
			d.append("FFFF");
			d.append("02");
			d.append("01");
			d.append("01");

			String hsmRespData = sendData2Hsm(d.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			int pukLen = Integer.parseInt(hsmRespData.substring(2, 6), 16) * 2;
			resp.setPublicKey(hsmRespData.substring(6, 6 + pukLen));
			resp.setPublicE(CrbUtil.long2HexString(exponent, 3).replaceFirst("0", ""));
			resp.setPrivatekey(hsmRespData.substring(10 + pukLen));

			return resp;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("genRsaKeyPair().", e);
		}
	}
	/**
	 * 生成公私钥对,且其密钥存储在加密机中
	 * @param moduleLen
	 *            模长
	 * @param exponent
	 *            公钥指数
	 * @param rsakeyindex
	 *            存储的加密机密钥索引号,其值范围为0~19
	 * @param pwd
	 *            访问该RSA密钥的口令(8个字符)
	 * @return
	 */
	public static HsmGenRsaPairResponse genRsaKeyPair(int moduleLen, int exponent, int rsakeyindex,
			String pwd) {
		HsmGenRsaPairResponse resp = null;
		try {
			resp = new HsmGenRsaPairResponse();
			StringBuilder d = new StringBuilder("D232");
			d.append(CrbUtil.long2HexString(moduleLen, 2));
			d.append(CrbUtil.long2HexString(exponent, 4));
			if(rsakeyindex!=-1)
			{
				d.append(CrbUtil.long2HexString(rsakeyindex, 2));
				d.append(CrbUtil.string2Ascii(pwd));
			}else
			{
				d.append("FFFF");
			}
			
			d.append("02");
			d.append("01");
			d.append("01");

			String hsmRespData = sendData2Hsm(d.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			int pukLen = Integer.parseInt(hsmRespData.substring(2, 6), 16) * 2;
			resp.setPublicKey(hsmRespData.substring(6, 6 + pukLen));
			resp.setPublicE(CrbUtil.long2HexString(exponent, 3).replaceFirst("0", ""));
			resp.setPrivatekey(hsmRespData.substring(10 + pukLen));

			return resp;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("genRsaKeyPair().", e);
		}
	}


	/**
	 * 用私钥加/解密操作
	 * 
	 * @param data
	 *            加/解密数据
	 * @param privateKey
	 *            私钥(采用LMK加密的DER格式)
	 * @param isEnc
	 *            是否加密
	 * @param paddingMode
	 *            填充模式<br>
	 *            <li>0: 不填充</li><br>
	 *            <li>1: PKCS#1填充算法</li><br>
	 *            <li>2: OAEP填充算法</li><br>
	 */
	public static void encryptRSAPrivateKey(String data, String privateKey, boolean isEnc,
			int paddingMode) {
		try {
			StringBuilder d = new StringBuilder("D20C");
			d.append(isEnc ? "01" : "00");
			d.append(CrbUtil.long2HexString(paddingMode, 1));
			d.append(CrbUtil.long2HexString(privateKey.length() >> 1, 2));
			d.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			d.append(privateKey);
			d.append(data);
			sendData2Hsm(d.toString());
		} catch (Exception e) {
			throw new HsmException("encryptRSAPrivateKey().", e);
		}
	}

	/**
	 * 产生ECC公私钥
	 * @return
	 */
	public static HsmGenEccKeyResponse genEccKey() {
		try {
			HsmGenEccKeyResponse resp = new HsmGenEccKeyResponse();
			String respData = sendData2Hsm("D45001");
			resultCheck(resp, respData, 0);

			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			int pukLen = Integer.parseInt(respData.substring(2, 6), 16) * 2;
			resp.setPubKey(respData.substring(6, 6 + pukLen));
			resp.setPriKey(respData.substring(10 + pukLen));
			return resp;
		} catch (Exception e) {
			throw new HsmException("HsmGenEccKeyResponse().", e);
		}
	}
	
	/**
	 * 产生Rsa公私钥
	 * 
	 * @param mode
	 *            0 取出公钥  1 取出私钥  2 取出公私钥
	 * @param index
	 *            rsa 密钥索引
         * @param pwd
	 *            访问该RSA密钥的口令(8个字符)	 * @return
	 */
	public static HsmGenRsaPairResponse outPutRsaKey(int mode ,int index ,String pwd) {
		HsmGenRsaPairResponse resp = null;
		try {
			resp = new HsmGenRsaPairResponse();
			StringBuilder d = new StringBuilder("D233");
			d.append(CrbUtil.long2HexString(index, 2));
			d.append(CrbUtil.long2HexString(mode, 1));
			d.append("01");
			d.append("01");
			d.append(CrbUtil.string2Ascii(pwd));

			String hsmRespData = sendData2Hsm(d.toString());
			resultCheck(resp, hsmRespData, 0);
			if (resp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return resp;
			}

			int pukLen = Integer.parseInt(hsmRespData.substring(2, 6), 16) * 2;
			resp.setPublicKey(hsmRespData.substring(6, 6 + pukLen));
			resp.setPublicE(null);
			resp.setPrivatekey(hsmRespData.substring(10 + pukLen));

			return resp;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("genRsaKeyPair().", e);
		}
	}
	
	//根据索引号得到RSA分量
	public static HashMap<String,String> RsaPrivateKeySeparation(int rsakeyindex,int kekindex) {
		HsmGenRsaPairResponse resp = null;
		try {
			resp = new HsmGenRsaPairResponse();
			StringBuilder msg = new StringBuilder("D263");
			msg.append(CrbUtil.long2HexString(3, 1));//加密算法类型
			msg.append(CrbUtil.long2HexString(rsakeyindex,2));
			msg.append(CrbUtil.long2HexString(kekindex, 2));
			msg.append(CrbUtil.long2HexString(0, 1));//分散次数		
			
			String hsmRespData = sendData2Hsm(msg.toString());
			resultCheck(resp, hsmRespData, 0);
			
			int DLen = Integer.parseInt(hsmRespData.substring(2, 6), 16) * 2;
			String d =hsmRespData.substring(6, 6 + DLen);
			hsmRespData= hsmRespData.substring(6 + DLen);
			
			int p1Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String p = hsmRespData.substring(4, 4 + p1Len);
			hsmRespData= hsmRespData.substring(4 + p1Len);
			
			int p2Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String q = hsmRespData.substring(4, 4 + p2Len);
			hsmRespData= hsmRespData.substring(4 + p2Len);
			
			int mod1Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String pe = hsmRespData.substring(4, 4 + mod1Len);
			hsmRespData= hsmRespData.substring(4 + mod1Len);
			
			int mod2Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String qe = hsmRespData.substring(4, 4 + mod2Len);
			hsmRespData= hsmRespData.substring(4 + mod2Len);
			
			int mod3Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String coeff = hsmRespData.substring(4, 4 + mod3Len);

				
			d=desAndTdesAlg(true, false, false, null,d, kekindex);
			p=desAndTdesAlg(true, false, false, null,p, kekindex);
			q=desAndTdesAlg(true, false, false, null,q, kekindex);
			pe=desAndTdesAlg(true, false, false, null,pe, kekindex);
			qe=desAndTdesAlg(true, false, false, null,qe, kekindex);
			coeff=desAndTdesAlg(true, false, false, null,coeff, kekindex);
			
			HashMap<String ,String> map = new HashMap<String,String>();
		    map.put("d",subFormDer(d) );
		    map.put("p",subFormDer(p) );
		    map.put("q",subFormDer(q) );
		    map.put("pe",subFormDer(pe) );
		    map.put("qe",subFormDer(qe) );
		    map.put("coeff",subFormDer(coeff) );

		    System.out.println("d="+d);
			System.out.println("d="+subFormDer(d));
			System.out.println("p="+subFormDer(p));
			System.out.println("q="+subFormDer(q));
			System.out.println("pe="+subFormDer(pe));
			System.out.println("qe="+subFormDer(qe));
			System.out.println("coeff="+subFormDer(coeff));
		    return map;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("RsaPrivateKeySeparation().", e);
		}
	}
	
	//根据私钥值得到RSA分量
	public static HashMap<String,String> RsaPrivateKeySeparation(String rsakeyLmk,int rsakeyLen,int kekindex) {
		HsmGenRsaPairResponse resp = null;
		try {
			resp = new HsmGenRsaPairResponse();
			StringBuilder msg = new StringBuilder("D263");
			msg.append(CrbUtil.long2HexString(3, 1));//加密算法类型
			msg.append("FFFF");
			msg.append(CrbUtil.long2HexString(kekindex, 2));
			msg.append(CrbUtil.long2HexString(0, 1));//分散次数		
			msg.append(CrbUtil.long2HexString(rsakeyLen, 2));//私钥长度				
			msg.append(rsakeyLmk);//私钥数据		
			
			String hsmRespData = sendData2Hsm(msg.toString());
			resultCheck(resp, hsmRespData, 0);
			
			int DLen = Integer.parseInt(hsmRespData.substring(2, 6), 16) * 2;
			String d =hsmRespData.substring(6, 6 + DLen);
			hsmRespData= hsmRespData.substring(6 + DLen);
			
			int p1Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String p = hsmRespData.substring(4, 4 + p1Len);
			hsmRespData= hsmRespData.substring(4 + p1Len);
			
			int p2Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String q = hsmRespData.substring(4, 4 + p2Len);
			hsmRespData= hsmRespData.substring(4 + p2Len);
			
			int mod1Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String pe = hsmRespData.substring(4, 4 + mod1Len);
			hsmRespData= hsmRespData.substring(4 + mod1Len);
			
			int mod2Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String qe = hsmRespData.substring(4, 4 + mod2Len);
			hsmRespData= hsmRespData.substring(4 + mod2Len);
			
			int mod3Len = Integer.parseInt(hsmRespData.substring(0, 4), 16) * 2;
			String coeff = hsmRespData.substring(4, 4 + mod3Len);
			
			
			d=desAndTdesAlg(true, false, false, null,d, kekindex);
			p=desAndTdesAlg(true, false, false, null,p, kekindex);
			q=desAndTdesAlg(true, false, false, null,q, kekindex);
			pe=desAndTdesAlg(true, false, false, null,pe, kekindex);
			qe=desAndTdesAlg(true, false, false, null,qe, kekindex);
			coeff=desAndTdesAlg(true, false, false, null,coeff, kekindex);
			
			HashMap<String ,String> map = new HashMap<String,String>();
			map.put("d",subFormDer(d) );
			map.put("p",subFormDer(p) );
			map.put("q",subFormDer(q) );
			map.put("pe",subFormDer(pe) );
			map.put("qe",subFormDer(qe) );
			map.put("coeff",subFormDer(coeff) );
			
			System.out.println("d="+d);
			System.out.println("d="+subFormDer(d));
			System.out.println("p="+subFormDer(p));
			System.out.println("q="+subFormDer(q));
			System.out.println("pe="+subFormDer(pe));
			System.out.println("qe="+subFormDer(qe));
			System.out.println("coeff="+subFormDer(coeff));
			return map;
		} catch (Exception e) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			throw new HsmException("RsaPrivateKeySeparation().", e);
		}
	}


	/**
	 * ECC签名
	 * @param priKey
	 * @param data
	 * @return
	 */
	public static String signUsingEcc(String priKey, String data) {
		try {
			StringBuilder b = new StringBuilder("D351");
			b.append(CrbUtil.long2HexString(priKey.length() >> 1, 2));
			b.append(priKey);
			b.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			b.append(data);
			String respData = sendData2Hsm(b.toString());
			if (!respData.substring(0, 2).equals("41")) {
				return null;
			}

			return respData.substring(6);
		} catch (Exception e) {
			throw new HsmException("signUsingEcc().", e);
		}
	}

	/**
	 * ECC验证签
	 * @param pubKey
	 * @param sinature
	 * @param data
	 * @return
	 */
	public static boolean verifyUsingEcc(String pubKey, String signature, String data) {
		try {
			StringBuilder sb = new StringBuilder("D352");
			sb.append(CrbUtil.long2HexString(pubKey.length() >> 1, 2));
			sb.append(pubKey);
			sb.append(CrbUtil.long2HexString(signature.length() >> 1, 2));
			sb.append(signature);
			sb.append(CrbUtil.long2HexString(data.length() >> 1, 2));
			sb.append(data);
			String respData = sendData2Hsm(sb.toString());
			if (!respData.subSequence(0, 2).equals("41")) {
				return false;
			}

			return true;
		} catch (Exception e) {
			throw new HsmException("verify().", e);
		}
	}

	/**
	 * HSM返回结果判断
	 * @param resp
	 * @param respData
	 * @param reserveLen
	 *            保留字长度,单位为字节
	 */
	private static void resultCheck(HsmResponse resp, String respData, int reserveLen) {
		if (respData == null || respData.length() == 0) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			Log.error(null, HsmSJJ1312BUtil.class, "HSM响应数据为null.");
			return;
		}

		if (!respData.substring(0, 2).equals("41")) {
			resp.setStatus(HsmResponse.RLT_FAIL);
			Log.error(null, HsmSJJ1312BUtil.class,
					"HSM响应错误码[" + respData.substring(reserveLen * 2 + 2, reserveLen * 2 + 4) + "].");
			System.out.println("HSM响应错误码["
					+ respData.substring(reserveLen * 2 + 2, reserveLen * 2 + 4) + "].");
			return;
		}
	}

	// public static String checkMac(int smkIndex, String key, String mac, String macCalcuData) {
	// StringBuilder sb = new StringBuilder("B0080000000000000000");
	// sb.append(CrbUtil.long2HexString(smkIndex, 2));
	// sb.append(key);
	// sb.append(CrbUtil.long2HexString(key.length() / 2, 2));
	// sb.append(mac);
	// sb.append(macCalcuData);
	// try {
	// return sendData2Hsm(sb.toString());
	// } catch (UnknownHostException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// } catch (IOException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// return null;
	// }

	/**
	 * 导入次主密钥
	 * @param kc1
	 *            密钥分量一
	 * @param kc2
	 *            密钥分量二
	 * @param kc3
	 *            密钥分量三
	 * @param keyIndex
	 *            导入密钥对应的索引号
	 * @param kekIndex
	 *            保护密钥索引号（已存在的次主密钥）
	 * @return
	 */
	public static boolean importSmk(int keyLenFlag,String kc1, String kc2, String kc3, int keyIndex, int kekIndex) {
		try {

			byte[] k1 = CrbUtil.hexString2Ba(kc1);
			byte[] k2 = CrbUtil.hexString2Ba(kc2);
			byte[] k3 = CrbUtil.hexString2Ba(kc3);
			k1 = xor(k1, k2);
			k1 = xor(k1, k3);

			String ecnKey = CrbUtil.ba2HexString(k1);

			ecnKey = desAndTdesAlg(true, false, true, null, ecnKey, kekIndex);
			HsmImportShsmkResponse smkImportResp = importShsmk(keyIndex, kekIndex, (byte) keyLenFlag,
					ecnKey, macForPboc3Des(ecnKey, kekIndex));
			if (smkImportResp == null || smkImportResp.getStatus() != HsmResponse.RLT_SUCCESS) {
				return false;
			}
			return true;
		} catch (Exception e) {
			throw new HsmException("importSmk().", e);
		}
	}

	public static byte[] xor(byte[] v1, byte[] v2) {
		for (int i = 0; i < v1.length; i++) {
			v1[i] = (byte) (v1[i] ^ v2[i]);
		}
		return v1;
	}

	/**
	 * PBOC MAC算法
	 * @param data
	 *            待计算数据
	 * @param keyIndex
	 *            加密密钥(次主密钥)
	 * @return
	 */
	public static String macForPboc3Des(String data, int keyIndex) {
		byte[] temp = CrbUtil.padding(CrbUtil.hexString2Ba(data), (byte) 0x80, (byte) 0x00, true);
		data = CrbUtil.ba2HexString(temp);
		String tempData = data.substring(0, data.length() - 16);
		String icv = "0000000000000000";
		for (int i = 0; i < tempData.length(); i += 16) {
			icv = desAndTdesAlg(false, true, true, icv, tempData.substring(i, i + 16), keyIndex);
			if (icv == null) {
				throw new RuntimeException("调用加密机进行DES运算失败!");
			}
		}

		String rlt = desAndTdesAlg(true, true, true, icv, data.substring(data.length() - 16),
				keyIndex);
		if (rlt == null) {
			throw new RuntimeException("调用加密机进行TDES运算失败!");
		}
		return rlt.substring(0, 8);
	}

	/**
	 * DES/3DES算法
	 * 
	 * @param isTdes
	 * @param isCbc
	 * @param isEncryption
	 * @param icv
	 * @param data
	 * @param smkKindex
	 * @return
	 */
	public static String desAndTdesAlg(boolean isTdes, boolean isCbc, boolean isEncryption,
			String icv, String data, int smkKindex) {
		HsmEncOrDecryUsingSmkResponse resp = null;
		if (isTdes) {// 3DES
			resp = encOrDecryUsingSmk(smkKindex, 0, false, null, isEncryption, isCbc ? 3 : 1, icv,
					data, null);
		} else {// DES
			resp = encOrDecryUsingSmk(smkKindex, 0, false, null, isEncryption, isCbc ? 2 : 0, icv,
					data, null);
		}

		if (resp == null || resp.getStatus() != HsmResponse.RLT_SUCCESS) {
			throw new RuntimeException("调用加密机加/解数据操作失败");
		}
		return resp.getData();
	}

	/**
	 * 向加密机发送请求
	 * 
	 * @param data
	 * @return
	 * @throws IOException
	 * @throws UnknownHostException
	 */
	public static String sendData2Hsm(String data) throws UnknownHostException, IOException {
		System.out.println(">>>" + data);
		Log.info(null, HsmSJJ1312BUtil.class, "send data to HSM>>>>: " + data);
		String ret = null;
		Socket socket = new Socket(Constants.HSM_SERVER_IP, Constants.HSM_SERVER_PORT);
		socket.setSoTimeout(Constants.HSM_TIMEOUT);
		socket.setTcpNoDelay(true);
		OutputStream os = socket.getOutputStream();
		InputStream is = socket.getInputStream();
		try {
			byte[] buf = CrbUtil.hexString2Ba(data);
			os.write(buf);
			os.flush();
			buf = new byte[1024];
			int len = is.read(buf);
			if (len > 0) {
				ret = CrbUtil.ba2HexString(buf, 0, len).toUpperCase();
			}
		} finally {
			if (os != null) {
				os.close();
			}
			if (is != null) {
				is.close();
			}
			if (socket != null) {
				socket.close();
			}
		}
		Log.info(null, HsmSJJ1312BUtil.class, "receive data from HSM<<<: " + ret);
		System.out.println("<<<" + ret);
		return ret;
	}
	
	private static String subFormDer (String data) {
    	String temp = "";
    	if (data.substring(data.length() - 1, data.length()).equals("0")) {
    		for (int i = data.length(); i > 0; i--) {
        		temp = data.substring(i - 1, i);
        		if (temp.equals("0")) {
        			continue;
        		} else if (temp.equals("8")){
        			data = data.substring(0, i - 1);
        			return data;
        		} else {
        			return data;
        		}
    		}
    	} else {
    		return data;
    	}
    	return data;
    }

	public static void main(String[] args) {
		importShsmk(100, 0, (byte) 2, "6B919DEA9E71DB49401A3198A67E8E67", "561BB764");
	}
}
